import { reactive, ref, watch, watchEffect } from 'vue';
import { TreeCheckMod } from '../../inner/Constaint.js';
import formFieldRef from '../../use/formFieldRef.js';

class CascaderStore {
  data = [];
  flatData = [];
  valueField = 'value';
  titleField = 'title';
  valMap = {};
  constructor(props, emit) {
    this.props = props;
    this.emit = emit;
    this.valueField = props.valueField || 'value';
    this.titleField = props.titleField || 'title';
    this.mode = props.mode ?? TreeCheckMod.HALF;
    const store = reactive({
      nodeMap: {},
      columns: [],
      filteredList: []
    });
    this.store = store;
    const selectedKey = ref([]);
    const value = formFieldRef(props, emit, []);
    this.selectedKey = selectedKey;
    this.value = value;
    this.init(props.data);
    this.valMap['__'] = this.data.map(item => item[this.valueField]);
    watch(() => props.data, () => {
      if (this.data !== props.data) {
        this.init(props.data);
        this.valMap['__'] = this.data.map(item => item[this.valueField]);
      }
      const keys = selectedKey.value.filter(key => store.nodeMap[key]);
      if (keys.length !== selectedKey.value.length) {
        selectedKey.value = keys;
      }
    });

    // 外部修改selected值进行同步
    watch(() => [selectedKey.value], () => {
      const keys = selectedKey.value;
      const columns = [this.valMap['__']];
      if (keys && keys.length) {
        keys.forEach(key => {
          if (this.valMap[key]) {
            columns.push(this.valMap[key]);
          } else {
            const item = store.nodeMap[key];
            if (item && item.children) {
              const levelIds = item.children.map(aItem => aItem[this.valueField]);
              this.valMap[key] = levelIds;
              columns.push(levelIds);
            }
          }
        });
      }
      store.columns = columns;
    }, {
      immediate: true
    });

    // 外部修改selected值进行同步
    watchEffect(() => {
      const val = value.value;
      if (props.multi) {
        this.setCheckedByMod(val);
      } else {
        selectedKey.value = val || [];
      }
    });
  }
  init(data) {
    this.data = data;
    this.flatData = this.getAllFlatNodes(this.data);
    this.store.filteredList = [];
    const map = {};
    this.buildRelation(this.data, null, 0, map);
    this.store.nodeMap = map;
  }

  /**
   * 构建父子关系和层级关系
   * @param data
   * @param parent
   * @param level
   */
  buildRelation = (data, parent, level, map) => {
    data.forEach(item => {
      map[item[this.valueField]] = item;
      item._parent = parent;
      item._level = level;
      if (item.children) {
        this.buildRelation(item.children, item, level + 1, map);
      }
    });
  };

  /**
   * 获取显示的树节点
   * @param nodes
   * @returns
   */
  getAllFlatNodes = nodes => {
    const list = nodes.flatMap(item => {
      if (item.children?.length) {
        return [item, this.getAllFlatNodes(item.children)].flat();
      } else {
        return [item];
      }
    });
    return list;
  };
  getStore() {
    return this.store;
  }
  clearSelect = () => {};

  /**
   * 过滤
   * @param keyword
   */
  filter(keyword) {
    if (keyword) {
      const allChildren = this.flatData.filter(item => !item.children || item.children.length === 0);
      const lines = allChildren.map(item => {
        const arr = [];
        arr.push(item);
        let parent = item._parent;
        while (parent) {
          arr.push(parent);
          parent = parent._parent;
        }
        arr.reverse();
        return arr;
      });
      const filteredList = lines.filter(line => {
        return line.some(item => item[this.titleField].includes(keyword));
      });
      console.log(filteredList);
      this.store.filteredList = filteredList;
    } else {
      this.store.filteredList = [];
    }
  }
  getNode = key => {
    return this.store.nodeMap[key];
  };

  /**
   * 选择节点
   * @param key
   */
  selectItem = key => {
    const node = this._getNode(key);
    if (node) {
      const vals = [];
      for (let i = 0; i < node._level; i++) {
        vals.push(this.selectedKey.value[i]);
      }
      vals[node._level] = node[this.valueField];
      this.selectedKey.value = vals;
    }
  };
  _getNode = nodeId => {
    let node;
    if (typeof nodeId === 'string' || typeof nodeId === 'number') {
      node = this.store.nodeMap[nodeId];
    } else {
      node = nodeId;
      nodeId = node[this.valueField];
    }
    return node;
  };

  /**
   * 更新节点选择状态
   * @param nodeId
   */
  updateNodeCheckStatus = nodeId => {
    if (!nodeId) {
      return;
    }
    const node = this._getNode(nodeId);
    if (node) {
      const n = this.store.nodeMap[node[this.valueField]];
      n.checked = this.getNodeChecked(n);
      this.setCheckedForwardUp(node);
    }
  };
  checkNode = (nodeId, checked) => {
    const node = this._getNode(nodeId);
    const n = this.store.nodeMap[node[this.valueField]];
    if (this.props.beforeChecked) {
      const result = this.props.beforeChecked(n, checked);
      if (result === false) {
        return;
      }
    }
    if (checked) {
      const addNum = {
        num: 0
      };
      this.setCheckedForwardDownNum(n, checked, addNum);
      this.setCheckedForwardUpNum(n, addNum);
      if (this.props.max && this.value.value.length + addNum.num + 1 > this.props.max) {
        this.emit('exceed');
        return;
      }
    }
    n.checked = checked;
    this.setCheckedForwardDown(n, checked);
    n.checked = this.getNodeChecked(node);
    this.setCheckedForwardUp(n);
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('change', checkedKeys);
  };
  setCheckedForwardDownNum = (node, checked, addNum) => {
    if (node.children) {
      node.children.forEach(item => {
        if (item.disabled) return;
        if (!item.checked) {
          addNum.num++;
          this.setCheckedForwardDownNum(item, checked, addNum);
        }
      });
    }
  };
  setCheckedForwardDown = (node, checked) => {
    if (node.children) {
      node.children.forEach(item => {
        if (item.disabled) return;
        this.store.nodeMap[item[this.valueField]].checked = checked;
        this.setCheckedForwardDown(item, checked);
      });
    }
  };
  getNodeChecked = nodeId => {
    const node = this._getNode(nodeId);
    if (!node.children || node.children.length === 0) {
      return node.checked;
    } else {
      let checked = false;
      let checkedNum = 0;
      let indeterminateNum = 0;
      node.children.forEach(item => {
        if (item.checked === true) {
          checkedNum++;
        }
        if (item.checked === 'indeterminate') {
          indeterminateNum++;
        }
      });
      if (checkedNum === node.children.length) {
        checked = true;
      } else if (checkedNum > 0) {
        checked = 'indeterminate';
      }
      if (!checked && indeterminateNum > 0) {
        checked = 'indeterminate';
      }
      return checked;
    }
  };
  getNodeChecked2 = nodeId => {
    const node = this._getNode(nodeId);
    if (!node.children || node.children.length === 0) {
      return node.checked;
    } else {
      let checked = false;
      let checkedNum = 0;
      let indeterminateNum = 0;
      node.children.forEach(item => {
        if (item.checked === true) {
          checkedNum++;
        }
        if (item.checked === 'indeterminate') {
          indeterminateNum++;
        }
      });
      if (checkedNum + 1 === node.children.length) {
        checked = true;
      } else if (checkedNum > 0) {
        checked = 'indeterminate';
      }
      if (!checked && indeterminateNum > 0) {
        checked = 'indeterminate';
      }
      return checked;
    }
  };
  setCheckedForwardUpNum = (node, addNum) => {
    const parentNode = node._parent;
    if (parentNode) {
      const checked = this.getNodeChecked2(parentNode);
      if (parentNode.checked !== checked) {
        addNum.num++;
        this.setCheckedForwardUpNum(parentNode, addNum);
      }
    }
  };
  setCheckedForwardUp = node => {
    const parentNode = node._parent;
    if (parentNode) {
      const checked = this.getNodeChecked(parentNode);
      this.store.nodeMap[parentNode[this.valueField]].checked = checked;
      this.setCheckedForwardUp(parentNode);
    }
  };
  checkAll = () => {
    for (const key in this.store.nodeMap) {
      this.store.nodeMap[key].checked = true;
    }
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('change', checkedKeys);
  };
  uncheckAll = () => {
    for (const key in this.store.nodeMap) {
      this.store.nodeMap[key].checked = false;
    }
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('change', checkedKeys);
  };
  loadData = async (node, loadDataMethod) => {
    try {
      const list = await loadDataMethod(node);
      if (list.length > 0) {
        node.children = list;
        list.forEach(item => {
          this.store.nodeMap[item[this.valueField]] = item;
        });
      }
    } catch (e) {
      //
    }
    this.store.nodeMap[node[this.valueField]].loading = false;
  };

  /**
   *
   * @param mode
   * @returns
   */
  getChecked = (mode = TreeCheckMod.HALF) => {
    if (mode === TreeCheckMod.FULL) {
      return this.getFullChecked();
    }
    if (mode === TreeCheckMod.CHILD) {
      return this.getChildChecked();
    }
    if (mode === TreeCheckMod.HALF) {
      return this.getHalfChecked();
    }
    if (mode === TreeCheckMod.SHALLOW) {
      return this.getShallowChecked();
    }
    return [];
  };

  /**
   * 获取所有选中的节点包含父节点和子节点
   * @returns
   */
  getFullChecked = () => {
    return this.flatData.filter(node => node.checked === true);
  };

  /**
   * 选中的子节点
   * @returns
   */
  getChildChecked = () => {
    return this.flatData.filter(node => node.checked === true && (!node.children || node.children.length === 0));
  };

  /**
   * 返回全部选中子节点和部分选中的父节点
   * @returns
   */
  getHalfChecked = () => {
    return this.flatData.filter(node => node.checked === true || node.checked === 'indeterminate');
  };

  /**
   * 如果父节点下所有子节点全部选中，只返回父节点
   * @returns
   */
  getShallowChecked = () => {
    const ret = [];
    this.flatData.forEach(node => {
      if (node.checked === true) {
        const parentChecked = (() => {
          const parent = node._parent;
          if (!parent) {
            return false;
          }
          return parent.checked === true;
        })();
        if (!parentChecked) {
          ret.push(node);
        }
      }
    });
    return ret;
  };

  /**
   * 选中的节点标识
   * @param mode
   * @returns
   */
  getCheckedKeys = (mode = TreeCheckMod.HALF) => {
    const nodes = this.getChecked(mode);
    return nodes.map(node => node[this.valueField]);
  };
  clearChecked = () => {
    this.flatData.forEach(node => {
      this.store.nodeMap[node[this.valueField]].checked = false;
    });
  };
  setCheckedByMod = val => {
    this.clearChecked();
    if (this.mode === TreeCheckMod.FULL) {
      this.setCheckedByFull(val);
    }
    if (this.mode === TreeCheckMod.HALF) {
      this.setCheckedByHalf(val);
    }
    if (this.mode === TreeCheckMod.CHILD) {
      this.setCheckedByChild(val);
    }
    if (this.mode === TreeCheckMod.SHALLOW) {
      this.setCheckedByShallow(val);
    }
  };
  setCheckedByFull = val => {
    val.forEach(key => {
      const n = this.store.nodeMap[key];
      if (!n) return;
      n.checked = true;
      this.setCheckedForwardUp(n);
    });
  };
  setCheckedByHalf = val => {
    val.forEach(key => {
      const node = this._getNode(key);
      if (!node) return;
      if (!node.children || node.children.length === 0) {
        const n = this.store.nodeMap[key];
        n.checked = true;
        this.setCheckedForwardUp(n);
      }
    });
  };
  setCheckedByChild = val => {
    val.forEach(key => {
      const node = this._getNode(key);
      if (!node) return;
      if (!node.children || node.children.length === 0) {
        const n = this.store.nodeMap[key];
        n.checked = true;
        this.setCheckedForwardUp(n);
      }
    });
  };
  setCheckedByShallow = val => {
    val.forEach(key => {
      const n = this.store.nodeMap[key];
      if (!n) return;
      n.checked = true;
      this.setCheckedForwardUp(n);
      this.setCheckedForwardDown(n, true);
    });
  };
}

export { CascaderStore };
